package cn.itcast.nems.payment.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.itcast.nems.common.util.LocalDateTimeUtils;
import cn.itcast.nems.manager.msas.MsasManager;
import cn.itcast.nems.oldems.api.EmsPaymentOrderApi;
import cn.itcast.nems.oldems.dto.EmsPaymentOrderDTO;
import cn.itcast.nems.order.dao.entity.OrderPaymentFlow;
import cn.itcast.nems.order.dto.PaymentFlowDTO;
import cn.itcast.nems.order.service.OrderPaymentFlowService;
import cn.itcast.nems.order.service.OrderService;
import cn.itcast.nems.payment.constant.PaymentSyncStateEnum;
import cn.itcast.nems.payment.dao.entity.PaymentSyncLog;
import cn.itcast.nems.payment.dto.PaymentSyncResultDTO;
import cn.itcast.nems.payment.service.PaymentEmsOnlineSyncService;
import cn.itcast.nems.payment.service.PaymentSyncLogService;
import cn.itcast.nems.studentrecord.constant.ReferenceTypeEnum;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecord;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecordOrderRelationship;
import cn.itcast.nems.studentrecord.dao.mapper.StudentRecordOrderRelationshipMapper;
import cn.itcast.nems.studentrecord.dto.StudentRecordDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordFundFlowDTO;
import cn.itcast.nems.studentrecord.enumeration.FundFlowPaymentTypeEnum;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordFundFlowService;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * <br>同步EMS线上支付流水 ，处理逻辑如下：</br>
 * <br>1、查询老EMS payment_order 记录和对应的 journal记录  ， 主要提取 payment_order表的 clazz_id、student_id 和 journal 表的 id</br>
 * <br>2、根据clazz_id 和student_id 查询 新EMS 【学籍ID】</br>
 * <br>3、根据学籍查询 e2_student_record_order_relation 表学籍订单关系记录 （一个学籍对应一个订单）</br>
 * <br>3.1、存在订单 ，且存在订单支付流水 ，根据订单ID 和 journal_no  查询 e2_order_payment_flow 记录 ， 不用处理</br>
 * <br>3.2、存在订单 ，不存在订单支付流水 ，调用创建订单流水接口 “ orderService.addPaymentFlow（）”， 同时产生学籍支付流水</br>
 * <br>4、不存在订单 ，调用 studentRecordFundFlowService.create(StudentRecordFundFlowDTO flowDTO) ，这种情况不用创建订单 ， 需要在学籍表的 “payment_no” 为  老EMS表 journal 表 id</br>
 * <br>注意点：studentRecordFundFlowService.create(StudentRecordFundFlowDTO flowDTO) ，  payment_no 使用 老EMS  payment_order 表ID</br>
 * </p>
 *
 * @author zhouwei
 * @since 2024/2/22 4:37 PM
 **/
@Service
@RequiredArgsConstructor
@Slf4j
public class PaymentEmsOnlineSyncServiceImpl implements PaymentEmsOnlineSyncService {
    private final StudentRecordFundFlowService studentRecordFundFlowService;
    private final EmsPaymentOrderApi emsPaymentOrderApi;
    private final StudentRecordService studentRecordService;
    private final PaymentSyncLogService paymentSyncLogService;
    private final OrderService orderService ;
    private final StudentRecordOrderRelationshipMapper studentRecordOrderRelationshipMapper ;
    private final OrderPaymentFlowService orderPaymentFlowService ;
    private final MsasManager msasManager ;
    @Value("${payment.online-payment-sync.notice-user}")
    private List<String> noticeUsers  ;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized List<PaymentSyncResultDTO> syncEmsOnlinePayment(String startTime) {
        Date time = StringUtils.hasText(startTime) ? DateUtil.parse(startTime, "yyyy-MM-dd HH:mm:ss") : null;
        if (Objects.isNull(time)) {
            Calendar calendar = Calendar.getInstance();
            // calendar.add(Calendar.DAY_OF_MONTH, -1);
            calendar.set(2024, Calendar.JANUARY,1,0,0,0);
            time = calendar.getTime();
            log.info("【同步老EMS 线上支付流水】起始时间：{}" ,  DateUtil.formatDateTime(time)) ;
        }
        // 历史出现异常的payment_order
        List<String> historyIds  =  paymentSyncLogService.listObjs(
                new LambdaQueryWrapper<PaymentSyncLog>().select(PaymentSyncLog::getPaymentOrderId)
                        .notIn(PaymentSyncLog::getState ,1).groupBy(PaymentSyncLog::getPaymentOrderId),Object::toString) ;
        List<EmsPaymentOrderDTO> emsPaymentOrderList = emsPaymentOrderApi.findListByCreatedTime(time ,CollectionUtil.isNotEmpty(historyIds) ? historyIds : null);
        if (CollectionUtil.isEmpty(emsPaymentOrderList)) {
            log.info("【同步老EMS 线上支付流水】起始时间：{} , 没有查询到老EMS 线上支付流水", DateUtil.formatDateTime(time));
            return null;
        }
        Set<Integer> studentIds = emsPaymentOrderList.stream().map(EmsPaymentOrderDTO::getStudentId).collect(Collectors.toSet());
        List<StudentRecordDTO> studentRecordList = Objects.requireNonNullElse(studentRecordService.queryByStudentIdAndClassId(studentIds, null), new ArrayList<>());
        final Map<Integer, List<StudentRecordDTO>> map = studentRecordList
                .stream()
                .collect(Collectors .groupingBy(StudentRecordDTO::getStudentId));
        List<PaymentSyncResultDTO> resultList = emsPaymentOrderList
                .stream()
                .map(item -> this.processPayment(map ,item))
                .toList();
        log.info("【同步老EMS 线上支付流水】处理结果：{}", JSON.toJSONString(resultList, SerializerFeature.DisableCircularReferenceDetect));
        // 发送企业消息
        // this.sendMessage(resultList);
        return resultList;
    }

    /**
     * <p>
     *  以下是老EMS在线支付流水数据同步失败的明细，请查收~
     * 【表格形式展示】日记账/支付流水ID、支付平台ID、支付方式、支付金额、支付时间、学生ID、班级ID、错误原因描述
     * </p>
     *
     * @author zhouwei
     * @since 2024/2/26 10:43 AM
     **/
    private void sendMessage (List<PaymentSyncResultDTO> list ) {
        list  =  list.stream()
                .filter(item -> ! PaymentSyncStateEnum.SUCCESS.equals(item.getSyncStateEnum()) && !PaymentSyncStateEnum.MANY_ORDER_PAYMENT.equals(item.getSyncStateEnum()))
                .toList() ;
        if(CollectionUtil.isEmpty(list )) {
            return;
        }
        StringBuilder message = new StringBuilder("以下是老EMS在线支付流水数据同步失败的明细，请查收~").append("\r\n") ;
        for (PaymentSyncResultDTO resultDTO : list) {
            message.append("日记账/支付流水ID:").append(resultDTO.getJournalId()).append("\r\n") ;
            // 支付平台ID
            message.append("支付平台ID:").append(resultDTO.getPaymentOrderId()).append("\r\n") ;
            // 支付方式
            message.append("支付方式:").append(resultDTO.getPayMethodCode()).append("\r\n") ;
            // 支付金额
            message.append("支付金额:").append(BigDecimal.valueOf(resultDTO.getAmount()).divide(BigDecimal.valueOf(100),2, RoundingMode.HALF_UP )).append("\r\n") ;
            // 支付时间
            message.append("支付时间:").append(Objects.nonNull(resultDTO.getConfirmtime())?DateUtil.formatDateTime(resultDTO.getConfirmtime()):"").append("\r\n");
            //学生ID
            message.append("学生ID:").append(resultDTO.getStudentId()).append("\r\n");
            // 班级ID
            message.append("班级ID:").append(resultDTO.getClazzId()).append("\r\n");
            // 重复原因
            message.append("错误原因描述:").append(resultDTO.getSyncStateEnum().getEnumValue()).append("\r\n") ;
            message.append("\r\n") ;

        }
        for (String user : noticeUsers) {
            msasManager.sendWechatMessage(user , message.toString())   ;
        }
    }

    private PaymentSyncResultDTO processPayment(Map<Integer, List<StudentRecordDTO>> map ,EmsPaymentOrderDTO item) {
        PaymentSyncResultDTO resultDTO = this.wrapPaymentSyncResultDTO(item ) ;
        // 根据同步日志表检查是否同步过线上支付流水
        List<PaymentSyncLog> logList = paymentSyncLogService.list(
                new LambdaQueryWrapper<PaymentSyncLog>()
                        .eq(PaymentSyncLog::getPaymentOrderId ,item.getId())
                        .eq(PaymentSyncLog::getDeletedFlag ,false)
                        .orderByDesc(PaymentSyncLog::getCreatedDatetime)) ;
        if (CollectionUtil.isNotEmpty(logList) && PaymentSyncStateEnum.SUCCESS.getEnumKey().equals(logList.get(0).getState())) {
            log.info("【同步老EMS 线上支付流水】同步日志已存在记录，详情：{}", JSON.toJSONString(logList, SerializerFeature.DisableCircularReferenceDetect));
            resultDTO.setSyncStateEnum(PaymentSyncStateEnum.MANY_ORDER_PAYMENT);
            resultDTO.setLogFlag(false);
            return resultDTO;
        }
        // 根据【凭单号】检查线上订单流水是否存在
        if(this.isExistOrderPayment(resultDTO.getJournalId())) {
            resultDTO.setContinueFlag(false);
            resultDTO.setLogFlag(true);
            resultDTO.setSyncStateEnum(PaymentSyncStateEnum.ELIMINATE_DIFF);
            return this.saveSyncLog(resultDTO ,logList , item) ;
        }
        List<StudentRecordDTO> recordList = Objects.requireNonNullElse(map.get(item.getStudentId()), new ArrayList<>());
        recordList = recordList.stream()
                .filter(i -> item.getClazzId().equals(i.getClazzId())).collect(Collectors.toList());
        PaymentSyncStateEnum syncStateEnum = PaymentSyncStateEnum.SUCCESS;
        // 如果不存在学籍 ，同步日志记录异常内容
        if (CollectionUtil.isEmpty(recordList)) {
            syncStateEnum = PaymentSyncStateEnum.NO_STUDENT_RECORD;
        }
        // 存在多个学籍
        if (CollectionUtil.isNotEmpty(recordList) && recordList.size() > 1) {
            syncStateEnum = PaymentSyncStateEnum.MANY_STUDENT_RECORD;
        }
        String studentRecordId = syncStateEnum.equals(PaymentSyncStateEnum.SUCCESS) ? recordList.get(0).getId() : null;
        resultDTO.setSyncStateEnum(syncStateEnum);
        resultDTO.setStudentRecordId(studentRecordId);
        // 学籍不存在
        if (Objects.isNull(studentRecordId)) {
            resultDTO.setContinueFlag(false);
            resultDTO.setLogFlag(true);
        }
        // 检查订单是否存在 ，如果存在维护订单流水
        if (resultDTO.getContinueFlag()) {
            this.processOrderPayment(resultDTO, item);
        }
        // 如果订单不存在 ，维护学籍流水
        if (resultDTO.getContinueFlag()) {
            this.processStudentRecordPayment(resultDTO, item);
        }

        return this.saveSyncLog(resultDTO ,logList , item) ;
    }

    private PaymentSyncResultDTO saveSyncLog(PaymentSyncResultDTO resultDTO ,List<PaymentSyncLog> logList,EmsPaymentOrderDTO item)  {
        if (resultDTO.getLogFlag() && !PaymentSyncStateEnum.MANY_ORDER_PAYMENT.equals(resultDTO.getSyncStateEnum())) {
            PaymentSyncLog paymentSyncLog = this.wrapPaymentSyncLog(item.getId(), item.getJournalId(), resultDTO.getSyncStateEnum());
            Integer state = null;
            if (CollectionUtil.isNotEmpty(logList)) {
                paymentSyncLog.setId(logList.get(0).getId());
                state = logList.get(0).getState();
            }
            if (Objects.isNull(state) || !state.equals(paymentSyncLog.getState())) {
                paymentSyncLogService.saveOrUpdate(paymentSyncLog);
            }
        }
        return resultDTO ;
    }

    private PaymentSyncResultDTO wrapPaymentSyncResultDTO(EmsPaymentOrderDTO item ) {
        return new PaymentSyncResultDTO(
                item.getId(),
                item.getJournalId(),
                item.getStudentId(),
                item.getClazzId(),
                null,
                Objects.nonNull(item.getAmount()) ? item.getAmount().multiply(BigDecimal.valueOf(100)).intValue() : 0,
                true,
                true,
                null,
                item.getPaymentchannelId(),
                item.getPaymentchannelName(),
                item.getConfirmtime(),
                item.getPayMethodCode(),
                item.getPayMethodName()
        );
    }

    /**
     * <p>
     * 根据 凭单号 检查 线上订单流水是否存在
     * </p>
     *
     * @author zhouwei
     * @since 2024/3/8 1:51 PM
     **/

    private Boolean isExistOrderPayment(Integer journalId)  {
        List<OrderPaymentFlow> flowList = orderPaymentFlowService
                .list(
                        new LambdaQueryWrapper<OrderPaymentFlow>()
                                .in(OrderPaymentFlow::getJournalType, Set.of(FundFlowPaymentTypeEnum.ONLINE_PAY.name(), "ONLINE"))
                                .eq(OrderPaymentFlow::getJournalNo, journalId)
                                .eq(OrderPaymentFlow::getDeletedFlag, false)
                );
        log.info("【同步老EMS 线上支付流水】根据[凭单号]检查线上订单流水是否存在， 查询结课：{}" , JSON.toJSONString(flowList ,SerializerFeature.DisableCircularReferenceDetect));
        return !CollectionUtils.isEmpty(flowList);
    }

    /**
     * <p>
     * 检查是否存在订单 ， 如果存在 维护订单流水
     * </p>
     *
     * @param resultDTO 参数（学籍ID）
     * @author zhouwei
     * @since 2024/2/23 1:53 PM
     **/
    private void processOrderPayment(PaymentSyncResultDTO resultDTO , EmsPaymentOrderDTO orderDTO) {
        List<StudentRecordOrderRelationship> relationshipList = studentRecordOrderRelationshipMapper.findListByStudentRecordId(resultDTO.getStudentRecordId());
        if (CollectionUtil.isEmpty(relationshipList)) {
            resultDTO.setContinueFlag(true);
            return;
        }
        StudentRecordOrderRelationship recordOrderRelationship = relationshipList.get(0);
        // 根据order_id 和 journal_no  查询 e2_order_payment_flow 记录 ，
        List<OrderPaymentFlow> flowList = orderPaymentFlowService
                .list(
                        new LambdaQueryWrapper<OrderPaymentFlow>()
                                .eq(OrderPaymentFlow::getOrderId, recordOrderRelationship.getOrderId())
                                .eq(OrderPaymentFlow::getJournalNo, resultDTO.getJournalId())
                                .eq(OrderPaymentFlow::getDeletedFlag, false));
        // 如果存在流水 ,不用做任何处理
        if (CollectionUtil.isNotEmpty(flowList)) {
            resultDTO.setContinueFlag(false);
            resultDTO.setLogFlag(true);
            resultDTO.setSyncStateEnum(PaymentSyncStateEnum.ELIMINATE_DIFF);
            return;
        }
        log.info("【同步老EMS 线上支付流水】调用添加订单流水参数 订单ID：{}, 流水参数：{}" ,
                recordOrderRelationship.getOrderId(),
                JSON.toJSONString(orderDTO,SerializerFeature.DisableCircularReferenceDetect));
        try {
            orderService.addPaymentFlow(this.createPaymentFlowDTO(recordOrderRelationship.getOrderId() , orderDTO));
            resultDTO.setContinueFlag(false);
        } catch (Exception e) {
            log.info("【同步老EMS 线上支付流水】调用添加订单流水 异常：{}" ,e.getMessage());
            resultDTO.setOtherStateEnum() ;
        }
    }

    /**
     * <p>
     * 如果不存在订单 ，维护学籍流水
     * result.setReferenceType(studentRecordJournal.getJournalType()); -- 枚举值：EMS_SYNC
     * result.setReferenceId(studentRecordJournal.getJournalId()); -- journal_id
     * result.setPaymentNo(studentRecordJournal.getJournalLineId()); -- payment_order_id
     * result.setReferenceLineId(studentRecordJournal.getJournalLineId()); -- journal_id
     * </p>
     *
     * @author zhouwei
     * @since 2024/2/23 1:59 PM
     **/
    private void processStudentRecordPayment(PaymentSyncResultDTO resultDTO ,EmsPaymentOrderDTO paymentOrderDTO) {

        StudentRecord studentRecord = studentRecordService.getById(resultDTO.getStudentRecordId()) ;
        StudentRecordFundFlowDTO dto = this.createStudentRecordFundFlow(studentRecord, paymentOrderDTO,resultDTO.getAmount());
        dto.setUpdateStudentRecord(true);
        log.info("【同步老EMS 线上支付流水】调用谈价学籍流水参数：{}" , JSON.toJSONString(dto  ,SerializerFeature.DisableCircularReferenceDetect)) ;
        try {
            studentRecordFundFlowService.create(dto) ;
        }catch (Exception e) {
            log.info("【同步老EMS 线上支付流水】调用谈价学籍流水 异常：{}" ,e.getMessage() );
            resultDTO.setOtherStateEnum() ;
        }

    }

    private PaymentSyncLog wrapPaymentSyncLog(String paymentOrderId, Integer journalId, PaymentSyncStateEnum stateEnum) {
        PaymentSyncLog paymentSyncLog = new PaymentSyncLog();
        paymentSyncLog.setPaymentOrderId(paymentOrderId);
        paymentSyncLog.setJournalId(journalId);
        switch (stateEnum) {
            case ELIMINATE_DIFF -> {
                paymentSyncLog.setState(PaymentSyncStateEnum.SUCCESS.getEnumKey());
                paymentSyncLog.setDescription(stateEnum.getEnumValue());
            }
            default -> {
                paymentSyncLog.setState(stateEnum.getEnumKey()) ;
                paymentSyncLog.setDescription(stateEnum.getEnumValue());
            }
        }

        return paymentSyncLog;
    }

    private PaymentFlowDTO createPaymentFlowDTO (String orderId , EmsPaymentOrderDTO emsPaymentOrderDTO){
        PaymentFlowDTO flowDTO = new PaymentFlowDTO();
        // e2_order id
        flowDTO.setOrderId(orderId);
        // ONLINE_PAY
        flowDTO.setJournalType(FundFlowPaymentTypeEnum.ONLINE_PAY.name());
        // 对应journal 表 id ， 老 ems 先生成 journal ，然后生成  payment_order
        flowDTO.setJournalNo(Objects.nonNull(emsPaymentOrderDTO.getJournalId()) ? String.valueOf(emsPaymentOrderDTO.getJournalId()) :null);
        // 支付金额 ，需要将【元】换算成【分】
        flowDTO.setAmount(Objects.nonNull(emsPaymentOrderDTO.getAmount()) ? emsPaymentOrderDTO.getAmount().multiply(BigDecimal.valueOf(100)).intValue(): 0);
        // 手续费
        flowDTO.setCharge(Objects.nonNull(emsPaymentOrderDTO.getCharge()) ? emsPaymentOrderDTO.getCharge().multiply(BigDecimal.valueOf(100)).intValue(): 0);
        // 手续费率
        flowDTO.setChargeRate(emsPaymentOrderDTO.getChargeRate());
        // journal 表 confirmtime
        flowDTO.setBizDate(Objects.nonNull(emsPaymentOrderDTO.getConfirmtime()) ?LocalDateTimeUtils.convertDateToLDT(emsPaymentOrderDTO.getConfirmtime()):null);
        // payment_order 的 pay_method_code
        flowDTO.setPaymentMethodCode(emsPaymentOrderDTO.getPayMethodCode());
        // payment_order 的 pay_method_name
        flowDTO.setPaymentMethodName(emsPaymentOrderDTO.getPayMethodName());
        // journal 表 paymentchannel_id
        flowDTO.setPaymentAccountCode(emsPaymentOrderDTO.getPaymentchannelId());
        // journal 表 paymentchannel_id 关联查一下
        flowDTO.setPaymentAccountName(emsPaymentOrderDTO.getPaymentchannelName());
        // flowDTO.setRemark(result.getRemark());
        return flowDTO ;
    }

    private  StudentRecordFundFlowDTO createStudentRecordFundFlow(StudentRecord studentRecord,
                                                                                     EmsPaymentOrderDTO paymentOrderDTO,
                                                                                     Integer amount ) {
        StudentRecordFundFlowDTO result = new StudentRecordFundFlowDTO();
        result.setStudentRecordId(studentRecord.getId());
        result.setAmount(amount);
        //        LocalDateTime bizDate = LocalDateTime.now();
        //        result.setBizDate(bizDate);
        result.setBizDate(LocalDateTimeUtils.convertDateToLDT(paymentOrderDTO.getConfirmtime()));
        result.setReferenceType(ReferenceTypeEnum.EMS_SYNC);
        result.setReferenceId(String.valueOf(paymentOrderDTO.getJournalId()));
        result.setReferenceLineId(paymentOrderDTO.getId());
        result.setClazzId(studentRecord.getClazzId());
        result.setClazzName(studentRecord.getClazzName());
        // 默认第一阶段
        result.setPhaseNumber("1");
        result.setPaymentType(FundFlowPaymentTypeEnum.ONLINE_PAY);
        // 对应  payment_order ID
        result.setPaymentNo(String.valueOf(paymentOrderDTO.getId()));
        result.setCharge(Objects.nonNull(paymentOrderDTO.getCharge())? paymentOrderDTO.getCharge().multiply(BigDecimal.valueOf(100)).intValue() : 0);
        result.setChargeRate(paymentOrderDTO.getChargeRate());
        result.setPaymentMethodCode(paymentOrderDTO.getPayMethodCode());
        result.setPaymentMethodName(paymentOrderDTO.getPayMethodName());
        result.setPaymentAccountCode(paymentOrderDTO.getPaymentchannelId());
        result.setPaymentAccountName(paymentOrderDTO.getPaymentchannelName());
        result.setRemark(FundFlowPaymentTypeEnum.ONLINE_PAY.getDesc());
        return result;
    }


}
